Skip to content

feat: port entity schema to gen-schema#563

Draft
sini wants to merge 18 commits into
denful:mainfrom
sini:feat/entity-gen-schema-port
Draft

feat: port entity schema to gen-schema#563
sini wants to merge 18 commits into
denful:mainfrom
sini:feat/entity-gen-schema-port

Conversation

@sini

@sini sini commented May 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Replace hand-rolled schemaEntryType in options.nix with gen-schema's mkSchemaOption (sidecars: includes, excludes; computed: isEntity)
  • Add flat-form entity declarations alongside legacy two-level form for both hosts and homes
  • Wire _topology and _meta introspection from gen-schema
  • Extract resolvedCtxModule to shared _types.nix for reuse across entity types

Details

Schema port: den.schema now uses gen-schema's mkSchemaEntryType which provides sidecar extraction, computed fields, and __functor wrapping. The resolvedCtxModule (id_hash, resolved, collisionPolicy) is extracted to _types.nix and injected into entity submodule imports.

Flat form: Both den.hosts and den.homes now accept flat declarations:

den.hosts.igloo = { system = "x86_64-linux"; users.tux = { }; };
den.homes."tux@igloo" = { system = "x86_64-linux"; };

A deepMergeAttrs custom type accepts both forms, and apply preprocesses flat entries into the canonical two-level shape via preprocessHosts. All 6 consumers see the unchanged { system.name = entity } shape.

Tests: 18 new tests (843 total, up from 825) covering flat hosts, flat homes, id_hash, freeform attrs, topology, meta introspection, isEntity computed, and schema sidecars.

Test plan

  • All 843 CI tests pass (nix develop -c just ci)
  • Flat host form produces correct two-level shape
  • Flat home form with @-name parsing works
  • Mixed flat + legacy forms coexist
  • Cross-entity host lookup from homes preserved
  • Existing templates (default, minimal, example) unaffected

@github-actions github-actions Bot added the allow-ci allow all CI integration tests label May 21, 2026
@sini sini force-pushed the feat/entity-gen-schema-port branch from 6574fac to a370d30 Compare May 21, 2026 22:37
@sini sini force-pushed the feat/entity-gen-schema-port branch 7 times, most recently from b2bcfd4 to 1b56211 Compare June 5, 2026 19:36
@sini sini force-pushed the feat/entity-gen-schema-port branch 3 times, most recently from 20fedec to 91ded60 Compare June 10, 2026 17:32
sini added 16 commits June 10, 2026 10:51
Replaces hand-rolled schemaEntryType with gen-schema mkSchemaOption.
Sidecars: includes, excludes. Computed: isEntity (structural content only).
Extracts resolvedCtxModule (id_hash, resolved, collisionPolicy) to
_types.nix for entity type reuse. collisionPolicy flows through deferred
module merge to entity instances (not a sidecar) preserving existing
ctx.host.collisionPolicy resolution path.
den.hosts now accepts both forms:
  - Legacy: den.hosts.x86_64-linux.igloo = { ... }
  - Flat:   den.hosts.igloo = { system = "x86_64-linux"; ... }

The outer option type uses a permissive submodule with deepMergeAttrs
freeformType (lib.recursiveUpdate-based merge that avoids the infinite
recursion lib.types.anything causes with cross-option references).
The apply function preprocesses flat entries into two-level form and
re-evaluates through the original attrsOf systemType, so all 6
consumers see the canonical { system.name = hostConfig } shape.
Same pattern as den.hosts: deepMergeAttrs + preprocessHosts + apply.
Cross-entity host lookup and osConfig injection preserved.
Covers: id_hash, freeform, topology, meta introspection,
isEntity computed, schema includes sidecar.
Update flake inputs and references to match the renamed repo
at github:sini/gen-schema.
gen-schema flattened _meta into _-prefixed options and renamed
sidecars → collections. nix-effects changed bindAttrs so true is a
literal param, not an optionality marker — translate __args values
to fx.bind.optionalArg before bind.fn.
Build host/user/home submodules with gen-schema's mkInstanceType, which
injects name, strict/freeform, _module.args.<kind>, and schema-owned
id_hash (gen-algebra mkIdentityModule) — replacing the hand-rolled id_hash
reflection in resolvedCtxModule. Identity now shares the exact algorithm
gen-schema's ref/setOf dedup compares against, removing the drift hazard
between den's copy and the library's.

Add an entity-gen-schema test pinning _roots: host is a root scope and
user/home are not (the buildRoots root-detection contract).
den.reservedKeys fed structuralKeysSet, which the pipeline classifier and
the synthetic-_ childKeys filter honored — but the aspect submodule's
freeform value type (aspectKeyType) wrapped every undeclared key into the
__contentValues/__provider provenance shape regardless. A reserved key was
excluded from dispatch but its value was still mangled, so consumers could
not read it back as the metadata den.reservedKeys promises.

Route structural/reserved keys through a passthrough merge (last def wins)
in aspectKeyType — the per-key dispatch the type comment already
anticipated. den.aspects.<name>.<reservedKey> now returns the raw value.
@sini sini force-pushed the feat/entity-gen-schema-port branch from 91ded60 to a22c657 Compare June 10, 2026 18:21
The `self` dispatch guard (fire-once at the registration scope; always bound
in ctx so flake-scope resolution policies can fire) was added on this branch
but never made it into origin/main's squash of the cherry-picked fixes — denful#603
covered spawn subtree routes and multi-def identity, not `self`. Rebasing onto
origin/main therefore silently dropped it.

Without `self`, `{ self, ... }:` policies never dispatch (flake isn't bound in
its own ctx), collapsing any flake-scope resolution cascade — e.g. a consumer's
flake -> fleet -> environment -> host walk produces zero host outputs.

Re-applies the dispatch.nix `self` injection and the policy/schema.nix
late-fan-out exclusion, plus the self-guard regression test.
schema-util and entities/_types each hand-rolled `filter (k != "conf" &&
!hasPrefix "_")` over `attrNames den.schema`. gen-schema already exposes
`_kindNames` (sorted, _-prefixed introspection keys excluded), so consume that
as the canonical kind list and drop the duplicated _-prefix filtering. Entity
detection still uses the schema's `isEntity` collection.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

allow-ci allow all CI integration tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant